//=============================================================================
//
//   Copyright (C) 2020 All rights reserved.
//
//   Filename:		data_parser.rs
//
//   Author: Wang Zhecheng - wangzhecheng@yeah.net
//
//   Date: 2020-10-09 21:29
//
//   Last Editors: Wang Zhecheng - wangzhecheng@yeah.net
//
//   Last modified:	2020-11-03 19:14
//
//   Description:
//
//=============================================================================
use serde_json::{from_reader, Value};
use std::fs::File;
use std::path::PathBuf;

pub trait DataParser {
    fn from_path(path: &str) -> Self
    where
        Self: Sized;
    fn from_str(data: &str) -> Self
    where
        Self: Sized;
    fn get_data(&self) -> &Value;
    fn get_path(&self) -> PathBuf;

    // -----------------------------
    /// get project directory
    fn get_proj_dir(&self) -> PathBuf {
        match self.get_path().parent() {
            None => panic!("there is no file name in {:?}", self.get_path()),
            Some(p) => PathBuf::from(p),
        }
    }
    /// get project name
    fn get_proj_name(&self) -> PathBuf {
        match self.get_path().file_stem() {
            None => panic!("there is no file name in {:?}", self.get_path()),
            Some(p) => PathBuf::from(p),
        }
    }

    /// get project configuration
    fn get_configuration(&self) -> &Value {
        self.get_data()
            .get("configuration")
            .expect("There is no \"configuration\" node in root json data")
    }
    /// get json node for materials
    #[inline]
    fn get_material_data(&self) -> &Value {
        self.get_data()
            .get("materials")
            .expect("There is no \"material\" node in root json data")
    }
    /// get json node for phases
    #[inline]
    fn get_phase_data(&self) -> &Value {
        self.get_data()
            .get("phases")
            .expect("There is no \"phases\" node in root json data")
    }
    /// get json node for topology
    #[inline]
    fn get_topology_data(&self) -> &Value {
        self.get_data()
            .get("topology")
            .expect("There is no \"topology\" node in root json data")
    }

    /// get json node for cracks
    #[inline]
    fn get_crack_data(&self) -> &Value {
        self.get_topology_data()
            .get("cracks")
            .expect("There is no \"cracks\" node in topology node")
    }
}

pub struct JsonParser {
    path: PathBuf,
    data: Value,
}

impl Default for JsonParser {
    #[inline]
    fn default() -> Self {
        Self {
            path: PathBuf::new(),
            data: Value::Null,
        }
    }
}

impl DataParser for JsonParser {
    #[inline]
    fn from_path(file_name_with_path: &str) -> Self
    where
        Self: Sized,
    {
        let path = PathBuf::from(file_name_with_path);
        let f = match File::open(path.clone()) {
            Ok(file) => file,
            Err(error) => panic!("Problem opening the file: {:?}", error),
        };
        let data = from_reader(f).expect("Failed to deserialize json file");
        Self { path, data }
    }
    #[inline]
    fn from_str(src: &str) -> Self
    where
        Self: Sized,
    {
        let data: Value = serde_json::from_str(src).expect("Failed to deserialize json str");
        let name = data
            .get("configuration")
            .expect("There is no \"configuration\" node in root json data")
            .get("name")
            .expect("There is no \"name\" node in configuration node")
            .as_str()
            .expect("Failed to convert project name to str");
        let mut path = PathBuf::from("result");
        path.push(name);
        path.push(name);
        path.set_extension("json");
        std::fs::create_dir_all(path.parent().expect("Failed to get parent path"))
            .expect("error on creat target directory");
        let mut f = std::fs::File::create(path.clone()).expect("error on creat target json file");
        match std::io::Write::write(&mut f, src.as_bytes()) {
            Err(why) => panic!("couldn't write to {}: {}", path.display(), why),
            Ok(_) => println!("successfully wrote to {}", path.display()),
        }
        Self { path, data }
    }
    #[inline]
    fn get_data(&self) -> &Value {
        &self.data
    }
    #[inline]
    fn get_path(&self) -> PathBuf {
        self.path.clone()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn read_file() {
        let parser = JsonParser::from_path("tool/gmshParser/example.json");
        assert_eq!(parser.get_data()["configuration"]["name"], "P52");
    }
    fn simple_input() -> String {
        r#"
        {
            "configuration": {
                "name": "P52",
                "method": "FEM",
                "unknown": ["Displace", "Pore"],
                "displace_pattern": ["Standard", "Signed", "Branch"],
                "equation": "Static",
                "solver": "Implicit",
                "load": "Incremental",
                "plane_stress": "false"
            },
            "io": {
                "log": { "log_level": "info", "detail_file_name": "" },
                "breakpoint": "",
                "output": { "path": "" }
            },
            "calculation": {
                "paralleled": "true",
                "time_step": { "theta": 0.8, "large_defomation": "false" },
                "iteration": {
                    "max_time": 100,
                    "nonlinear_scheme": "initial_stress",
                    "tolerance": { "relative": 0.01, "absolute": 0 }
                },
                "xfem": {
                    "active": "true",
                    "branch_enrich_radius": 0,
                    "crack_segment": { "default_delta_length": 0.03, "gauss_num": 4 },
                    "sif_integral": {
                        "domain": { "shape": "circle", "radius": 9 },
                        "qfunction": { "exponent": 1 }
                    }
                },
                "gravity_scheme": "k0",
                "water_density": { "value": 10, "unit": "kN/m^3" },
                "atm": { "value": 100, "unit": "kPa" }
            },
            "topology": {
                "shape_function_degree": 1,
                "elem_nodes_order": "clockwise",
                "elem_gauss_num": [
                    { "name": "Segment", "value": 3 },
                    { "name": "Triangle", "value": 3 },
                    { "name": "Quadrangle", "value": 4 },
                    { "name": "Tetrahedral", "value": 4 },
                    { "name": "Tri_prism", "value": 6 },
                    { "name": "Octahedral", "value": 8 }
                ],
                "cracks": [],
                "nodes": [
                    { "id": 11, "coord": [0, 0, 0] },
                    { "id": 6, "coord": [0, -2, 0] },
                    { "id": 7, "coord": [2, 0, 0] },
                    { "id": 8, "coord": [0, 2, 0] },
                    { "id": 9, "coord": [-2, 0, 0] },
                    { "id": 0, "coord": [-2, -2, 0] },
                    { "id": 1, "coord": [2, -2, 0] },
                    { "id": 2, "coord": [2, 2, 0] },
                    { "id": 3, "coord": [-2, 2, 0] },
                    { "id": 10, "coord": [0, 4, 0] },
                    { "id": 4, "coord": [-2, 4, 0] },
                    { "id": 5, "coord": [2, 4, 0] }
                ],
                "elements": [
                    { "id": 0, "type": "quadrangle", "mat": 1, "nodes": [0, 6, 11, 9] },
                    { "id": 1, "type": "quadrangle", "mat": 1, "nodes": [9, 11, 8, 3] },
                    { "id": 2, "type": "quadrangle", "mat": 1, "nodes": [6, 1, 7, 11] },
                    { "id": 3, "type": "quadrangle", "mat": 1, "nodes": [11, 7, 2, 8] },
                    { "id": 4, "type": "quadrangle", "mat": 2, "nodes": [2, 5, 10, 8] },
                    { "id": 5, "type": "quadrangle", "mat": 2, "nodes": [8, 10, 4, 3] }
                ]
            },
            "materials": [
                {
                    "id": 1,
                    "type": "DuncanChang",
                    "volume_weight": 19,
                    "friction_angle": 30,
                    "dilation_angle": 30,
                    "cohesion": 10,
                    "perm_x0": 1e-5,
                    "perm_x1": 1e-5,
                    "perm_x2": 1e-5,
                    "zeta": 0.1,
                    "k_ic": 2.9,
                    "elastic_modulus": 1000000.0,
                    "elastic_poisson": 0.3,
                    "rf": 201,
                    "k": 202,
                    "n": 203,
                    "g": 204,
                    "f": 205,
                    "d": 206
                },
                {
                    "id": 2,
                    "type": "elastic",
                    "volume_weight": 19,
                    "friction_angle": 30,
                    "dilation_angle": 30,
                    "cohesion": 10,
                    "perm_x0": 1e-5,
                    "perm_x1": 1e-5,
                    "perm_x2": 1e-5,
                    "zeta": 0.1,
                    "k_ic": 2.9,
                    "elastic_modulus": 1000000.0,
                    "elastic_poisson": 0.3
                }
            ],
            "init_condition": { "init_displacement": "", "init_pore": "" },
            "phases": [
                {
                    "id": 1,
                    "start_time": 0,
                    "end_time": 10,
                    "delta_time": 10,
                    "element_num": 9,
                    "boundary_condition": {
                        "dirichlet": [
                            { "node": 6, "dof": "x0", "type": "Linear", "start": 0.0, "end": 1.0 },
                            { "node": 0, "dof": "x0", "type": "Linear", "start": 0.0, "end": 1.0 },
                            { "node": 1, "dof": "x0", "type": "Linear", "start": 0.0, "end": 1.0 }
                        ],
                        "neumann": []
                    }
                }
            ]
        }
            "#
        .to_string()
    }
}
